/*
 * Copyright (c) 2007-2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.sun.org.apache.bcel.internal.classfile;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache BCEL" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache BCEL", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import com.sun.org.apache.bcel.internal.Constants;
import java.io.*;
import java.util.zip.*;

/**
 * Wrapper class that parses a given Java .class file. The method <A
 * href ="#parse">parse</A> returns a <A href ="JavaClass.html">
 * JavaClass</A> object on success. When an I/O error or an
 * inconsistency occurs an appropiate exception is propagated back to
 * the caller.
 *
 * The structure and the names comply, except for a few conveniences,
 * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
 * JVM specification 1.0</a>. See this paper for
 * further details about the structure of a bytecode file.
 *
 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
 */
public final class ClassParser {
    private DataInputStream file;
    private ZipFile zip;
    private String file_name;
    private int class_name_index, superclass_name_index;
    private int major, minor; // Compiler version
    private int access_flags; // Access rights of parsed class
    private int[] interfaces; // Names of implemented interfaces
    private ConstantPool constant_pool; // collection of constants
    private Field[] fields; // class fields, i.e., its variables
    private Method[] methods; // methods defined in the class
    private Attribute[] attributes; // attributes defined in the class
    private boolean is_zip; // Loaded from zip file

    private static final int BUFSIZE = 8192;

    /**
     * Parse class from the given stream.
     *
     * @param file Input stream
     * @param file_name File name
     */
    public ClassParser(InputStream file, String file_name) {
        this.file_name = file_name;

        String clazz = file.getClass().getName(); // Not a very clean solution ...
        is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");

        if (file instanceof DataInputStream) // Is already a data stream
            this.file = (DataInputStream) file;
        else
            this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
    }

    /** Parse class from given .class file.
     *
     * @param file_name file name
     * @throws IOException
     */
    public ClassParser(String file_name) throws IOException {
        is_zip = false;
        this.file_name = file_name;
        file = new DataInputStream(new BufferedInputStream(new FileInputStream(file_name), BUFSIZE));
    }

    /** Parse class from given .class file in a ZIP-archive
     *
     * @param file_name file name
     * @throws IOException
     */
    public ClassParser(String zip_file, String file_name) throws IOException {
        is_zip = true;
        zip = new ZipFile(zip_file);
        ZipEntry entry = zip.getEntry(file_name);

        this.file_name = file_name;

        file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), BUFSIZE));
    }

    /**
     * Parse the given Java class file and return an object that represents
     * the contained data, i.e., constants, methods, fields and commands.
     * A <em>ClassFormatException</em> is raised, if the file is not a valid
     * .class file. (This does not include verification of the byte code as it
     * is performed by the java interpreter).
     *
     * @return Class object representing the parsed class file
     * @throws  IOException
     * @throws  ClassFormatException
     */
    public JavaClass parse() throws IOException, ClassFormatException {
        /****************** Read headers ********************************/
        // Check magic tag of class file
        readID();

        // Get compiler version
        readVersion();

        /****************** Read constant pool and related **************/
        // Read constant pool entries
        readConstantPool();

        // Get class information
        readClassInfo();

        // Get interface information, i.e., implemented interfaces
        readInterfaces();

        /****************** Read class fields and methods ***************/
        // Read class fields, i.e., the variables of the class
        readFields();

        // Read class methods, i.e., the functions in the class
        readMethods();

        // Read class attributes
        readAttributes();

        // Check for unknown variables
        //Unknown[] u = Unknown.getUnknownAttributes();
        //for(int i=0; i < u.length; i++)
        //  System.err.println("WARNING: " + u[i]);

        // Everything should have been read now
        //      if(file.available() > 0) {
        //        int bytes = file.available();
        //        byte[] buf = new byte[bytes];
        //        file.read(buf);

        //        if(!(is_zip && (buf.length == 1))) {
        //          System.err.println("WARNING: Trailing garbage at end of " + file_name);
        //          System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
        //        }
        //      }

        // Read everything of interest, so close the file
        file.close();
        if (zip != null)
            zip.close();

        // Return the information we have gathered in a new object
        return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor, access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip ? JavaClass.ZIP : JavaClass.FILE);
    }

    /**
     * Read information about the attributes of the class.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readAttributes() throws IOException, ClassFormatException {
        int attributes_count;

        attributes_count = file.readUnsignedShort();
        attributes = new Attribute[attributes_count];

        for (int i = 0; i < attributes_count; i++)
            attributes[i] = Attribute.readAttribute(file, constant_pool);
    }

    /**
     * Read information about the class and its super class.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readClassInfo() throws IOException, ClassFormatException {
        access_flags = file.readUnsignedShort();

        /* Interfaces are implicitely abstract, the flag should be set
         * according to the JVM specification.
         */
        if ((access_flags & Constants.ACC_INTERFACE) != 0)
            access_flags |= Constants.ACC_ABSTRACT;

        if (((access_flags & Constants.ACC_ABSTRACT) != 0) && ((access_flags & Constants.ACC_FINAL) != 0))
            throw new ClassFormatException("Class can't be both final and abstract");

        class_name_index = file.readUnsignedShort();
        superclass_name_index = file.readUnsignedShort();
    }

    /**
     * Read constant pool entries.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readConstantPool() throws IOException, ClassFormatException {
        constant_pool = new ConstantPool(file);
    }

    /**
     * Read information about the fields of the class, i.e., its variables.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readFields() throws IOException, ClassFormatException {
        int fields_count;

        fields_count = file.readUnsignedShort();
        fields = new Field[fields_count];

        for (int i = 0; i < fields_count; i++)
            fields[i] = new Field(file, constant_pool);
    }

    /******************** Private utility methods **********************/

    /**
     * Check whether the header of the file is ok.
     * Of course, this has to be the first action on successive file reads.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readID() throws IOException, ClassFormatException {
        int magic = 0xCAFEBABE;

        if (file.readInt() != magic)
            throw new ClassFormatException(file_name + " is not a Java .class file");
    }

    /**
     * Read information about the interfaces implemented by this class.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readInterfaces() throws IOException, ClassFormatException {
        int interfaces_count;

        interfaces_count = file.readUnsignedShort();
        interfaces = new int[interfaces_count];

        for (int i = 0; i < interfaces_count; i++)
            interfaces[i] = file.readUnsignedShort();
    }

    /**
     * Read information about the methods of the class.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readMethods() throws IOException, ClassFormatException {
        int methods_count;

        methods_count = file.readUnsignedShort();
        methods = new Method[methods_count];

        for (int i = 0; i < methods_count; i++)
            methods[i] = new Method(file, constant_pool);
    }

    /**
     * Read major and minor version of compiler which created the file.
     * @throws  IOException
     * @throws  ClassFormatException
     */
    private final void readVersion() throws IOException, ClassFormatException {
        minor = file.readUnsignedShort();
        major = file.readUnsignedShort();
    }
}
